﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using MoneyWentWhere.DataModel;
using System.Diagnostics;
using MoneyWentWhere.WinFormsUI.Properties;
using System.Collections;
using MoneyWentWhere.DataModel.Tx;
using Equin.ApplicationFramework;

namespace MoneyWentWhere.WinFormsUI.UI.DataGrid
{
    public partial class TxSettingsPanel : UserControl,
        IAutoCompletePopup<TxSettings>
    {
        int tc = 0; // trace counter

        // Temporary items while the panel is open

        Transaction _tx;
        TxSettings _settings;
        bool _useFilter = false;
        
        List<String> _categories;

        IAutoCompletePopupHost<TxSettings> _host;

        public TxSettingsPanel()
        {
            InitializeComponent();
        }

        #region IAutoCompletePopup Members

        public void ShowPopup(IAutoCompletePopupHost<TxSettings> host)
        {
            if (host == null) { throw new ArgumentNullException("host"); }
            if (_host == host) { return; }

            _host = host;
            _host.AutoCompleteTokenChanged += OnAutoCompleteTokenChanged;

            // Initialize complete UI
            IDataGridViewEditingControl ec = _host as IDataGridViewEditingControl;
            if (ec == null) { return; }

            _tx = CastToTransaction(ec.EditingControlDataGridView.
                    Rows[ec.EditingControlRowIndex].DataBoundItem);

            if (_tx == null)
            {
                // In the ADDING NEW case
                BindingSource bs = ec.EditingControlDataGridView.DataSource as BindingSource;
                if (bs != null) { _tx = CastToTransaction(bs.Current); }

                if (_tx == null) { throw new ArgumentException("No transaction exists."); }
            }

            _settings = _host.AutoCompleteToken;
            _useFilter = false;

            UpdateNormalTab();
            UpdateReconcileTab();
            UpdateTransferTab();

            UpdateGuiFromSettings();
        }

        static Transaction CastToTransaction(object item)
        {
            Transaction tx = item as Transaction;
            if (tx != null) { return tx; }
            ObjectView<Transaction> view = item as ObjectView<Transaction>;
            if (view != null) { return view.Object; }
            return null;
        }

        public void ClosePopup()
        {
            // BUGFIX: calling ClosePopup from within 
            // the panel itself must close the form.
            Form f = FindForm();
            if (f != null && f.Visible) { f.Hide(); }

            // Unsubscribe events
            if (_host != null)
            {
                _host.AutoCompleteTokenChanged -= OnAutoCompleteTokenChanged;
            }

            _tx = null;
            _settings = null;
            _categories = null;

            _host = null;
        }

        public void ProcessInputKey(Keys key)
        {
            // Select items in the listbox
            ListBox listBox = null;
            if (tabs.SelectedTab == tabNormal) { listBox = lbCategory; }
            if (tabs.SelectedTab == tabTransfer) { listBox = lbToAccount; }

            if (listBox != null)
            {
                if (key == Keys.Down)
                {
                    if (listBox.SelectedIndex < listBox.Items.Count - 1)
                    {
                        listBox.SelectedIndex++;
                    }
                    return;
                }
                if (key == Keys.Up)
                {
                    if (listBox.SelectedIndex > 0)
                    {
                        listBox.SelectedIndex--;
                    }
                    return;
                }
            }

        }

        public bool PopupWantsInputKey(Keys key)
        {
            if (tabs.SelectedTab == tabNormal ||
                tabs.SelectedTab == tabTransfer)
            {
                return key == Keys.Down || key == Keys.Up;
            }
            
            return false;
        }

        public void CommitValue()
        {
            _settings = CreateSettingsFromGui();
            _host.AutoCompleteToken = _settings;
        }

        #endregion

        TxSettings CreateSettingsFromGui()
        {
            TransactionKind kind;

            if (tabs.SelectedTab == tabNormal) { kind = TransactionKind.Normal; }
            else if (tabs.SelectedTab == tabReconcile) { kind = TransactionKind.Reconcile; }
            else if (tabs.SelectedTab == tabTransfer) { kind = TransactionKind.Transfer; }
            else { throw new InvalidOperationException("Unsupported tab: " + tabs.SelectedTab); }

            switch (kind)
            { 
                case TransactionKind.Normal:
                    String cat = lbCategory.SelectedItem as String;
                    return new NormalTxSettings(cat);

                case TransactionKind.Reconcile:
                    ReconcileTxSettings rs = new ReconcileTxSettings();
                    decimal acctBalance;
                    if (decimal.TryParse(tbAcctBalance.Text, out acctBalance))
                    {
                        rs.Balance = acctBalance;
                    }
                    return rs;

                case TransactionKind.Transfer:
                    Account acct = lbToAccount.SelectedItem as Account;

                    if (ShouldShowTransferXRPanel())
                    {
                        return new TransferTxSettings(acct, ToAmount);
                    }
                    return new TransferTxSettings(acct);
            }
            return null;
        }

        void OnAutoCompleteTokenChanged(object sender, EventArgs e)
        {
            _settings = _host.AutoCompleteToken;
            _useFilter = true;
            UpdateGuiFromSettings();
        }

        List<String> Categories
        {
            get
            {
                if (_categories == null)
                {
                    Transaction tx = _tx;
                    if (tx == null) { return null; }
                    _categories = new List<string>(tx.DataSet.Categories);
                    _categories.Sort();
                }
                return _categories;
            }
        }

        void UpdateGuiFromSettings()
        {
            // Prevent stealing of focus
            bool returnFocus = (this.ParentForm != null && 
                Form.ActiveForm != this.ParentForm);

            switch (_settings.Kind)
            {
                case TransactionKind.Normal:
                    tabs.SelectedTab = tabNormal;
                    UpdateNormalTab();
                    break;

                case TransactionKind.Reconcile:
                    tabs.SelectedTab = tabReconcile;
                    UpdateReconcileTab();
                    break;
                
                case TransactionKind.Transfer:
                    tabs.SelectedTab = tabTransfer;
                    UpdateTransferTab();
                    break;
            }

            if (returnFocus) { _host.ReturnFocus(); }
        }

        void UpdateNormalTab()
        {
            NormalTxSettings settings = _settings as NormalTxSettings;
            if (settings == null) { settings = NormalTxSettings.Parse(""); }

            lbCategory.SelectedIndex = -1;
            lbCategory.Items.Clear();

            Transaction tx = _tx;
            if (tx == null) { return; }
            if (Categories == null) { return; }

            int selectedIndex = -1;
            settings.FillAutoComplete(lbCategory.Items, out selectedIndex, tx, Categories, _useFilter);
            lbCategory.SelectedIndex = selectedIndex;
        }

        void UpdateReconcileTab()
        {
            ReconcileTxSettings settings = _settings as ReconcileTxSettings;
            if (settings != null && settings.BalanceSpecified)
            {
                tbAcctBalance.Text = settings.Balance.ToString("N2");
                lblBalanceDate.Text = String.Format("at {0:g}", _tx.Date);
            } 
            else if (_tx != null)
            {
                tbAcctBalance.Text = _tx.AccountBalance.ToString("N2");
                lblBalanceDate.Text = String.Format("at {0:g}", _tx.Date);
            }
            else
            {
                tbAcctBalance.Text = "";
                lblBalanceDate.Text = ""; // Consider an amount from new TX
            }
        }

        void UpdateTransferTab()
        {
            TransferTxSettings settings = _settings as TransferTxSettings;
            if (settings == null) { settings = TransferTxSettings.Parse(""); }

            lbToAccount.SelectedIndex = -1;
            lbToAccount.Items.Clear();

            Transaction tx = _tx;
            if (tx == null) { return; }

            int selectedIndex = -1;
            settings.FillAutoComplete(lbToAccount.Items, out selectedIndex, tx, _useFilter);
            lbToAccount.SelectedIndex = selectedIndex;
            UpdateTransferInfoPanel();
        }

        #region TransferInfo

        void UpdateTransferInfoPanel()
        {
            TransferTxSettings settings = _settings as TransferTxSettings;
            if (settings == null) { settings = TransferTxSettings.Parse(""); }

            Account toAccount = lbToAccount.SelectedItem as Account;
            if (toAccount == null) { return; }

            bool toAcctCredited = true;
            if (_tx.Amount >= 0) { toAcctCredited = false; }
            lblTransferTitle.Text = (toAcctCredited ? "To" : "From") + " Account";

            bool showXR = SetTransferXRPanelVisible();
            if (showXR)
            {
                bnDirection.Image = toAcctCredited ? 
                    Resources.arrow_left : Resources.arrow_right;

                // From
                tbAmount.Text = Math.Abs(_tx.Amount).ToString("N2");
                lblCurrency.Text = _tx.Account.Currency.Id;

                // For the To amount, get from transferToTx or guess
                if (settings != null && settings.AmountSpecified)
                {
                    // Case 1: specified in settings
                    ToAmount = settings.Amount;
                }
                else if (_tx.TransferToTx != null && toAccount == _tx.TransferToTx.Account)
                {
                    // Case 2: specified in Tx (if it's the current one)
                    ToAmount = _tx.TransferToTx.Amount;
                }
                else
                {
                    // Case 3: determine XR from previous TX (if any)
                    decimal xr = _tx.DataSet.GetExchangeRate(_tx.Account.Currency, toAccount.Currency, _tx.Date);

                    if (xr <= 0)
                    {
                        // TODO: warn info doesn't exist
                        xr = 1;
                    }
                    ToAmount = -(xr * _tx.Amount);
                }
                lblToCurrency.Text = toAccount.Currency.Id;

                ExchangeRate = Currency.ExchangeRate(_tx.Amount, ToAmount);
                lblXRCurrencies.Text = toAccount.Currency + " = 1 " + _tx.Account.Currency;
            }
            else
            {
                lblCurrency.Text = "";
            }
        }

        decimal _exchangeRate;
        decimal ExchangeRate
        {
            get { return _exchangeRate; }
            set
            {
                _exchangeRate = value;

                // Invert for display (may switch to manual later)
                tbXR.TextChanged -= tbXR_TextChanged;                

                decimal xr = Math.Abs(_exchangeRate);
                tbXR.Text = xr.ToString("N2");
                if (xr < 1 && xr != 0)
                {
                    tbXR.Text = "1/" + (1/xr).ToString("N2");
                }
                tbXR.TextChanged += tbXR_TextChanged;
            }
        }

        private void tbXR_TextChanged(object sender, EventArgs e)
        {
            decimal xr;

            bool invert = false;
            String rep = tbXR.Text.Trim();
            if (rep.StartsWith("1/"))
            {
                rep = rep.Substring(2);
                invert = true;
            }

            if (decimal.TryParse(rep, out xr))
            {
                if (xr == 0) { return; }
                if (invert) { xr = 1 / xr; }

                _exchangeRate = xr;
                ToAmount = -(xr * _tx.Amount);
            }
        }

        decimal _toAmount;
        decimal ToAmount
        {
            get { return _toAmount; }
            set
            {
                _toAmount = value;

                tbToAmount.TextChanged -= tbToAmount_TextChanged;
                tbToAmount.Text = Math.Abs(ToAmount).ToString("N2");
                tbToAmount.TextChanged += tbToAmount_TextChanged;
            }
        }

        private void tbToAmount_TextChanged(object sender, EventArgs e)
        {
            decimal amount;
            if (decimal.TryParse(tbToAmount.Text, out amount) && amount != 0)
            {
                // TODO: consider sign
                _toAmount = amount;
                ExchangeRate = Currency.ExchangeRate(_tx.Amount, _toAmount);
            }
        }

        private void lbToAccount_SelectedIndexChanged(object sender, EventArgs e)
        {
            UpdateTransferInfoPanel();
        }

        bool ShouldShowTransferXRPanel()
        {
            bool visible = false;
            Account toAccount = lbToAccount.SelectedItem as Account;
            if (_tx != null && toAccount != null &&
                _tx.Amount != 0 &&
                _tx.Account.Currency != toAccount.Currency)
            {
                visible = true;
            }
            return visible;
        }

        bool SetTransferXRPanelVisible()
        {
            bool visible = ShouldShowTransferXRPanel();

            Trace.WriteLine("SetXRPanelVisible: " + visible);

            panelTransferXR.Visible = visible;
            if (visible)
            {
                lbToAccount.Height = tabTransfer.Height - lbToAccount.Top - panelTransferXR.Height;
            }
            else
            {
                lbToAccount.Height = tabTransfer.Height - lbToAccount.Top;
            }
            return visible;
        }

        #endregion

        private void lbToAccount_Click(object sender, EventArgs e)
        {
            if (!ShouldShowTransferXRPanel() && lbCategory.SelectedItem != null)
            {
                CommitValue();
                ClosePopup();
            }
        }

        private void OnClick_CommitAndClose(object sender, EventArgs e)
        {
            CommitValue();
            ClosePopup();
        }

        private void OnEnterKeyUp_CommitAndClose(object sender, KeyEventArgs e)
        {
            if (e.KeyData == Keys.Enter)
            {
                CommitValue();
                ClosePopup();
            }
        }
    }
}
